/*
 * Created on 2007-12-25
 *
 * TODO 
 */
package jacky.lanlan.song.domain;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import jacky.lanlan.song.closure.Factory;
import jacky.lanlan.song.reflection.proxy.ProxyFactory;

/**
 * 通用 JavaBean 生成器，通过 JDK Proxy 实现 JavaBean 的生成，以此来减少手工
 * 编写或者通过IDE自动生成的JavaBean类。
 * @author Jacky.Song
 */
public final class GenericJavaBeanMaker {

	private static class JavaBeanProxy implements InvocationHandler {
		private Map<String, Object> properties = new ConcurrentHashMap<String, Object>();
		
		public Object invoke(@SuppressWarnings("unused")Object proxy, 
				Method method, Object[] args) throws Throwable {
			String mName = method.getName();
			if (mName.startsWith("get")) {
				String prop=mName.substring(3);
				return properties.get(prop);//从properties里面查询Getter返回值
			}
			else if(mName.startsWith("is")) {//处理boolean类型属性读取
				String prop=mName.substring(2);
				return properties.get(prop);
			}
			else if (mName.startsWith("set")) {
				String prop=mName.substring(3);
				properties.put(prop, args[0]);//保存设置值到properties
			} 
			return null;
		}
	}

	/**
	 * 通过 JDK Proxy 实现通用的JavaBean。
	 * <p>
	 * 该方法将根据指定接口(包含getter/setter方法)创建一个 JavaBean Proxy，在底层，这个Proxy
	 * 使用HashMap保存由 setter 设置的值，并在 getter 请求值的时候，返回Map中的对应值。
	 * <p>
	 * 使用这个方法创建JavaBean的优势在于，可以省略对JavaBean接口实现类的手工编写，减少很多不必要的
	 * 代码生成。
	 * @param <T> 接口类型
	 * @param iface JavaBean接口，一般包含读取和设置Bean属性的getter/setter方法
	 * @return 接口的JavaBean实现
	 */
	public static <T> T generate(Class<T> iface) {
		return ProxyFactory.createProxy(new JavaBeanProxy(), iface);
	}

	/**
	 * 创建生成代理JavaBean的工厂。
	 * <p>
	 * 因为有JavaBean接口，所以如果使用 JDK Proxy 的方式来实现JavaBean的话，可以把创建逻辑
	 * 包装到JavaBean接口里面，这时，就可以用这个方法，在接口里面添加一个生成JavaBean的Factory，
	 * 比如：<br/>
	 * <code><pre>
	 * public interface Person{
	 *  //getter...
	 *  //setter...
	 *  Factory<Person> Impl=GenericJavaBeanMaker.factory(Person.class);
	 * }
	 * </pre>
	 * </code>
	 * 这样，在需要JavaBean的时候，就可以用<code>Person.Impl.create()</code>来创建一个JavaBean。
	 * @param <T> JavaBean接口类型
	 * @param iface JavaBean接口，一般包含读取和设置Bean属性的getter/setter方法
	 * @return 生成实现了iface的代理JavaBean的工厂
	 */
	public static <T> Factory<T> factory(final Class<T> iface){
		return new Factory<T>() {
			public T create() {
				return generate(iface);
			}
		};
	}

}
